using UnityEngine;

namespace M13.Phys
{
    public enum AABBType
    {
        Entity,
        Block
    }
    public class AABB
    {
        public Bounds bound;
        public AABBType Type { get; private set; }
        
        public bool CollisionNextFrame { get; private set; }
        
        public float StepHeight{ get; private set; }
        
        private static readonly float Epsilon = 1.0E-6f;

        private float _maxStepHeight = 1.0f;
        
        
        public AABB(Bounds bound, AABBType type)
        {
            this.bound = bound;
            Type = type;
        }

        public AABB(Vector3 origin, Vector3 size, AABBType type)
        {
            Bounds bound = new Bounds(origin, size);
            this.bound = bound;
            Type = type;
        }

        public void Move(ref Vector3 wantMove)
        {
            StepHeight = 0f;
            wantMove.x = ClipXCollide(wantMove.x);
            wantMove.z = ClipZCollide(wantMove.z);
            wantMove.y = wantMove.y == 0f ? StepHeight : (ClipYCollide(wantMove.y) == 0f ? StepHeight : ClipYCollide(wantMove.y));
            
            bound.center += wantMove;
        }
        
        public bool BottomContact()
        {
            bool contact = false;
            foreach(var thatBox in DispersedBoundsManager.DispersedBounds)
            {
                if(thatBox.Equals(this)) continue;
                
                if (thatBox.GetMax().x > GetMin().x && thatBox.GetMin().x < GetMax().x &&
                    thatBox.GetMax().z > GetMin().z && thatBox.GetMin().z < GetMax().z)
                {
                    if (Mathf.Abs(thatBox.GetMax().y - GetMin().y) < Epsilon * 2f)
                    {
                        contact = true;
                    }
                }
            }
            return contact;
        }
        public float ClipXCollide(float wantX)
        {
            if(wantX == 0f) return 0f;
            
            foreach (var thatBox in DispersedBoundsManager.DispersedBounds)
            {
                //要碰撞的盒子是自己就判断下一个
                if (thatBox.Equals(this)) continue;
                //判断在移动方向上的盒子
                if (wantX > 0f && GetMin().x < thatBox.GetMax().x)
                {
                    
                    if (thatBox.GetMax().y > GetMin().y && thatBox.GetMin().y < GetMax().y &&
                        thatBox.GetMax().z > GetMin().z && thatBox.GetMin().z < GetMax().z)
                    {
                        float actualX = 0f;
                        if (thatBox.GetMin().x - GetMax().x > Epsilon)
                        {
                            // actualX = (CollisionNextFrame = !(thatBox.GetMin().x - GetMax().x > wantX))
                            //     ? thatBox.GetMin().x - GetMax().x - Epsilon / 2f
                            //     : wantX - Epsilon / 2f;
                            if (CollisionNextFrame = !(thatBox.GetMin().x - GetMax().x > wantX))
                            {
                                actualX = thatBox.GetMin().x - GetMax().x - Epsilon / 2f;
                                Step(thatBox);
                            }
                            else
                            {
                                actualX = wantX - Epsilon / 2f;
                            }
                            
                        }
                        //如果两盒子已经重叠
                        else if (GetMax().x - thatBox.GetMin().x > Epsilon && thatBox.GetMax().x - GetMin().x > Epsilon)
                        {
                            if (GetCenter().x < thatBox.GetCenter().x)
                                actualX = -(GetMax().x - thatBox.GetMin().x);
                            if (GetCenter().x > thatBox.GetCenter().x)
                                actualX = thatBox.GetMax().x - GetMin().x;
                        }
                        else
                        {
                            actualX = 0;
                        }
                        wantX = actualX;
                    }
                }
                if (wantX < 0f && GetMax().x > thatBox.GetMin().x)
                {
                    if (thatBox.GetMax().y > GetMin().y && thatBox.GetMin().y < GetMax().y &&
                        thatBox.GetMax().z > GetMin().z && thatBox.GetMin().z < GetMax().z)
                    {
                        float actualX = 0f;
                        if (GetMin().x - thatBox.GetMax().x > Epsilon)
                        {
                            // actualX = (CollisionNextFrame = !(thatBox.GetMax().x - GetMin().x < wantX))
                            //     ? thatBox.GetMax().x - GetMin().x + Epsilon / 2f
                            //     : wantX + Epsilon / 2f;

                            if (CollisionNextFrame = !(thatBox.GetMax().x - GetMin().x < wantX))
                            {
                                actualX = thatBox.GetMax().x - GetMin().x + Epsilon / 2f;
                                Step(thatBox);
                            }
                            else
                            {
                                actualX = wantX + Epsilon / 2f;
                            }
                        }
                        //如果两盒子已经重叠
                        else if (GetMax().x - thatBox.GetMin().x > Epsilon && thatBox.GetMax().x - GetMin().x > Epsilon)
                        {
                            if (GetCenter().x < thatBox.GetCenter().x)
                                actualX = -(GetMax().x - thatBox.GetMin().x);
                            if (GetCenter().x > thatBox.GetCenter().x)
                                actualX = thatBox.GetMax().x - GetMin().x;
                        }
                        else
                        {
                            actualX = 0;
                        }
                        wantX = actualX;
                    }
                }
            }
            return wantX;
        }
        public float ClipYCollide(float wantY)
        {
            if (wantY == 0f) return 0f;

            foreach (var thatBox in DispersedBoundsManager.DispersedBounds)
            {
                // 要碰撞的盒子是自己就判断下一个
                if (thatBox.Equals(this)) continue;
                
                // 判断在移动方向上的盒子
                if (wantY > 0f && GetMin().y < thatBox.GetMax().y)
                {
                    if (thatBox.GetMax().x > GetMin().x && thatBox.GetMin().x < GetMax().x &&
                        thatBox.GetMax().z > GetMin().z && thatBox.GetMin().z < GetMax().z)
                    {
                        float actualY = 0f;
                        if (thatBox.GetMin().y - GetMax().y > Epsilon)
                        {
                            actualY = (CollisionNextFrame = !(thatBox.GetMin().y - GetMax().y > wantY))
                                ? thatBox.GetMin().y - GetMax().y - Epsilon / 2f
                                : wantY - Epsilon / 2f;
                        }
                        // 如果两盒子已经重叠
                        else if (GetMax().y - thatBox.GetMin().y > Epsilon && thatBox.GetMax().y - GetMin().y > Epsilon)
                        {
                            if (GetCenter().y < thatBox.GetCenter().y)
                                actualY = -(GetMax().y - thatBox.GetMin().y);
                            if (GetCenter().y > thatBox.GetCenter().y)
                                actualY = thatBox.GetMax().y - GetMin().y;
                        }
                        else
                        {
                            actualY = 0;
                        }
                        wantY = actualY;
                    }
                }

                if (wantY < 0f && GetMax().y > thatBox.GetMin().y)
                {
                    if (thatBox.GetMax().x > GetMin().x && thatBox.GetMin().x < GetMax().x &&
                        thatBox.GetMax().z > GetMin().z && thatBox.GetMin().z < GetMax().z)
                    {
                        float actualY = 0f;
                        if (GetMin().y - thatBox.GetMax().y > Epsilon)
                        {
                            actualY = (CollisionNextFrame = !(thatBox.GetMax().y - GetMin().y < wantY))
                                ? thatBox.GetMax().y - GetMin().y + Epsilon / 2f
                                : wantY + Epsilon / 2f;
                        }
                        // 如果两盒子已经重叠
                        else if (GetMax().y - thatBox.GetMin().y > Epsilon && thatBox.GetMax().y - GetMin().y > Epsilon)
                        {
                            if (GetCenter().y < thatBox.GetCenter().y)
                                actualY = -(GetMax().y - thatBox.GetMin().y);
                            if (GetCenter().y > thatBox.GetCenter().y)
                                actualY = thatBox.GetMax().y - GetMin().y;
                        }
                        else
                        {
                            actualY = 0;
                        }
                        wantY = actualY;
                    }
                }
                
            }
            return wantY;
        }
        public float ClipZCollide(float wantZ)
        {
            if (wantZ == 0f) return 0f;

            foreach (var thatBox in DispersedBoundsManager.DispersedBounds)
            {
                // 要碰撞的盒子是自己就判断下一个
                if (thatBox.Equals(this)) continue;
                
                // 判断在移动方向上的盒子
                if (wantZ > 0f && GetMin().z < thatBox.GetMax().z)
                {
                    if (thatBox.GetMax().y > GetMin().y && thatBox.GetMin().y < GetMax().y &&
                        thatBox.GetMax().x > GetMin().x && thatBox.GetMin().x < GetMax().x)
                    {
                        float actualZ = 0f;
                        if (thatBox.GetMin().z - GetMax().z > Epsilon)
                        {
                            // actualZ = (CollisionNextFrame = !(thatBox.GetMin().z - GetMax().z > wantZ))
                            //     ? thatBox.GetMin().z - GetMax().z - Epsilon / 2f
                            //     : wantZ - Epsilon / 2f;
                            if (CollisionNextFrame = !(thatBox.GetMin().z - GetMax().z > wantZ))
                            {
                                actualZ = thatBox.GetMin().z - GetMax().z - Epsilon / 2f;
                                Step(thatBox);
                            }
                            else
                            {
                                actualZ = wantZ - Epsilon / 2f;
                            }
                        }
                        // 如果两盒子已经重叠
                        else if (GetMax().z - thatBox.GetMin().z > Epsilon && thatBox.GetMax().z - GetMin().z > Epsilon)
                        {
                            if (GetCenter().z < thatBox.GetCenter().z)
                                actualZ = -(GetMax().z - thatBox.GetMin().z);
                            if (GetCenter().z > thatBox.GetCenter().z)
                                actualZ = thatBox.GetMax().z - GetMin().z;
                        }
                        else
                        {
                            actualZ = 0;
                        }
                        wantZ = actualZ;
                    }
                }
                
                if (wantZ < 0f && GetMax().z > thatBox.GetMin().z)
                {
                    if (thatBox.GetMax().y > GetMin().y && thatBox.GetMin().y < GetMax().y &&
                        thatBox.GetMax().x > GetMin().x && thatBox.GetMin().x < GetMax().x)
                    {
                        float actualZ = 0f;
                        if (GetMin().z - thatBox.GetMax().z > Epsilon)
                        {
                            // actualZ = (CollisionNextFrame = !(thatBox.GetMax().z - GetMin().z < wantZ))
                            //     ? thatBox.GetMax().z - GetMin().z + Epsilon / 2f
                            //     : wantZ + Epsilon / 2f;
                            if (CollisionNextFrame = !(thatBox.GetMax().z - GetMin().z < wantZ))
                            {
                                actualZ = thatBox.GetMax().z - GetMin().z + Epsilon / 2f;
                                Step(thatBox);
                            }
                            else
                            {
                                actualZ = wantZ + Epsilon / 2f;
                            }
                        }
                        // 如果两盒子已经重叠
                        else if (GetMax().z - thatBox.GetMin().z > Epsilon && thatBox.GetMax().z - GetMin().z > Epsilon)
                        {
                            if (GetCenter().z < thatBox.GetCenter().z)
                                actualZ = -(GetMax().z - thatBox.GetMin().z);
                            if (GetCenter().z > thatBox.GetCenter().z)
                                actualZ = thatBox.GetMax().z - GetMin().z;
                        }
                        else
                        {
                            actualZ = 0;
                        }
                        wantZ = actualZ;
                    }
                }
            }
            return wantZ;
        }

        public void Step(AABB thatAABB)
        {
            if (thatAABB.Type == AABBType.Entity) return;
            if (thatAABB.GetMax().y - GetMin().y < Epsilon + _maxStepHeight)
            {
                StepHeight = thatAABB.GetMax().y - GetMin().y + Epsilon;
            }
        }
        
        public Vector3 GetCenter()
        {
            return bound.center;
        }

        public Vector3 GetSize()
        {
            return bound.size;
        }
        public Vector3 GetMin()
        {
            return bound.min;
        }

        public Vector3 GetMax()
        {
            return bound.max;
        }
        
    }
}